module Maze
  module PngFormatter
    class Sigma
      include DrawingHelpers

      attr_reader :maze, :image, :lines

      def initialize(maze)
        @maze    = maze
        @image   = ChunkyPNG::Image.new(width, height, background_colour)
        @lines   = []

        draw_image
      end

      private

      def width
        if 0 == maze.width % 2
          (1.5 * maze.width * LENGTH + 0.5 * LENGTH + padding_offset).to_i
        else
          horizontal_units = (1..maze.width).map do |cell_number|
            0 == cell_number % 2 ? 1 : 2
          end.inject do |s, x|
            s + x
          end

          (horizontal_units * LENGTH +  padding_offset).to_i
        end
      end

      def height
        (maze.height * 2 * LENGTH + LENGTH + padding_offset).to_i
      end

      def origins
        @origins ||= compute_origin_points
      end

      def compute_origin_points
        points = Array.new(maze.height) { Array.new(maze.width) }

        maze.height.times do |row|
          maze.width.times do |column|
            points[row][column] =
              if 0 == column
                first_origin_point(row)
              else
                next_horizontal_origin_point(points[row][column - 1], column)
              end
          end
        end

        points
      end

      def first_origin_point(row)
        x = (LENGTH + PADDING).to_i
        y = (LENGTH + row * 2 * LENGTH + PADDING).to_i

        Point.new(x, y)
      end

      def next_horizontal_origin_point(previous_point, current_column)
        previous_point.update do |next_origin|
          next_origin.x += (1.5 * LENGTH).to_i
          next_origin.y += (current_column % 2 == 0 ? -LENGTH : LENGTH).to_i
        end
      end

      #
      # Compute corner points traversing in the clock-wise direction
      #
      #   0 _____  1
      #    /     \
      # 5 /   +   \ 2
      #   \ origin/
      #  4 \_____/ 3
      #
      # Once the origin is known the points 0 - 5 through can be computed
      #
      # Output: [x0, y0, x1, y1, ... , x5, y5, x0, y0]
      #         Is the closed path of the hexagon
      #
      def corner_points(origin)
        faces   = []
        offsets = [ [ -LENGTH / 2, -LENGTH ],
                    [  LENGTH / 2, -LENGTH ],
                    [  LENGTH,           0 ],
                    [  LENGTH / 2,  LENGTH ],
                    [ -LENGTH / 2,  LENGTH ],
                    [ -LENGTH,           0 ],
                    [ -LENGTH / 2, -LENGTH ] ]

        offsets.each do |(offset_x, offset_y)|
          faces << origin.update do |corner|
            corner.x += offset_x
            corner.y += offset_y
          end
        end
        faces
      end

      #
      # Group adjacent corner points into pairs
      #
      def face_line_segment(origin)
        corner_points(origin).each_cons(2)
      end

      #
      # Traces the clockwise directions of the hexagon w.r.t the closed path
      #
      def clockwise_directions
        [ maze.class::N, maze.class::NE, maze.class::SE,
          maze.class::S, maze.class::SW, maze.class::NW ]
      end

      def filter_open_faces(line_segments, open = [])
        line_segments.reject.each_with_index do |segment, i|
          open.include? clockwise_directions[i]
        end
      end

      def closed_line_segments(row, column)
        filter_open_faces( face_line_segment(origins[row][column]),
                           maze.grid[row][column] )
      end
    end
  end
end


